#include <stdio.h>

#include <openssl/x509.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>

#include "create_cert.h"

#define GENERATE_KEY_SUCCESS 0
#define GENERATE_KEY_FAIL -1


void mem_free(EVP_PKEY *pkey, X509 *x509)
{
    EVP_PKEY_free(pkey);
    X509_free(x509);
}

int createPKey(EVP_PKEY *pkey)
{
    RSA *rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
    printf("RSA_F4 : %d\n", (int)RSA_F4);
    
    if (!rsa)
    {
        perror("generate rsa key fail");
        return GENERATE_KEY_FAIL;
    }

    EVP_PKEY_assign_RSA(pkey, rsa);     
    //The RSA structure will be automatically freed when the EVP_PKEY structure is freed.

    return GENERATE_KEY_SUCCESS;
}

int createCert()
{
    X509 *x509 = X509_new();

    /*
     * set serial number of certificate
     */
    ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);

    /*
     * set valid time of certificate
     */
    //set time not before the current time
    X509_gmtime_adj(X509_get_notBefore(x509), 0);
    //set time not after a year later
    X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);

    /*
     * set public key of certificate
     */
    EVP_PKEY *pkey = EVP_PKEY_new();
    int key_ret = createPKey(pkey);
    if (key_ret == GENERATE_KEY_FAIL)
    {
        mem_free(pkey, x509);
        return GENERATE_CERT_FAIL;
    }
    X509_set_pubkey(x509, pkey);

    /*
     * since this is a SSC, set subject of certificate
     */
    X509_NAME *name = X509_get_subject_name(x509);
    //set "CA" to country
    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)"CN/CHN", -1, -1, 0);
    //set "WHALEY" to organization
    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)"WHALEY", -1, -1, 0);
    //set "xiaomi" to common name
    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)"xiaomi", -1, -1, 0);
    X509_set_issuer_name(x509, name);

    /*
     * set sign of certificate
     */
    X509_sign(x509, pkey, EVP_sha1());
    
    /*
     * write private key to disk
     */
    FILE *priKey_fp = fopen("key.pem", "wb");
    if (!priKey_fp)
    {
        mem_free(pkey, x509);
        return WRITE_FILE_FAIL;
    }
    PEM_write_PrivateKey(
        priKey_fp, 
        pkey, 
        NULL, //EVP_des_ede3_cbc(),     //set NULL if not encrypt this file 
        NULL, //"I am decrypt key",     //set NULL if not encrypt this file 
        10, 
        NULL, 
        NULL
    );
    fclose(priKey_fp);

    /*
     * write certificate to disk
     */
    FILE *cert_fp = fopen("cert.pem", "wb");
    if (!cert_fp)
    {
        mem_free(pkey, x509);
        return WRITE_FILE_FAIL;
    }
    PEM_write_X509(
        cert_fp,
        x509
    );
    fclose(cert_fp);

    mem_free(pkey, x509);
    return GENERATE_CERT_SUCCESS;

}
